package com.jxm.jdk8features;


import org.junit.Test;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;

/**
 * @Author: jxm
 * @Description:
 * @Date: 2023-12-21 16:39
 * @Version: 1.0
 */
public class StreamAPITest {



    //创建 Stream方式一：通过集合
    @Test
    public void test1(){
        List<Employee> employees = EmployeeData.getEmployees();

//        default Stream<E> stream() : 返回一个顺序流
        Stream<Employee> stream = employees.stream();

//        default Stream<E> parallelStream() : 返回一个并行流
        Stream<Employee> parallelStream = employees.parallelStream();
    }

    //创建 Stream方式二：通过数组
    @Test
    public void test2(){
        int[] arr = new int[]{1,2,3,4,5,6};
        //调用Arrays类的static <T> Stream<T> stream(T[] array): 返回一个流
        IntStream stream = Arrays.stream(arr);

        Employee e1 = new Employee(1001,"Tom");
        Employee e2 = new Employee(1002,"Jerry");
        Employee[] arr1 = new Employee[]{e1,e2};
        Stream<Employee> stream1 = Arrays.stream(arr1);
    }

    //创建 Stream方式三：通过Stream的of()
    @Test
    public void test3(){
        Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6);
    }


    //创建 Stream方式四：创建无限流
    @Test
    public void test4(){
//      迭代
//      public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f)
        //遍历前10个偶数
        Stream.iterate(0, t -> t + 2).limit(10).forEach(System.out::println);
//      生成
//      public static<T> Stream<T> generate(Supplier<T> s)
        Stream.generate(Math::random).limit(10).forEach(System.out::println);
    }


    //1-筛选与切片
    @Test
    public void test5(){
        List<Employee> list = EmployeeData.getEmployees();
//        filter(Predicate p)——接收 Lambda ， 从流中排除某些元素。
        Stream<Employee> stream = list.stream();
        //练习：查询员工表中薪资大于7000的员工信息
        stream.filter(e -> e.getSalary() > 7000).forEach(System.out::println);

        System.out.println();
//        limit(n)——截断流，使其元素不超过给定数量。
        list.stream().limit(3).forEach(System.out::println);
        System.out.println();

//        skip(n) —— 跳过元素，返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个，则返回一个空流。与 limit(n) 互补
        list.stream().skip(5).forEach((employee)->System.out.println("====:"+employee.getName()));

        System.out.println();
//        distinct()——筛选，通过流所生成元素的 hashCode() 和 equals() 去除重复元素

        list.add(new Employee(1010,"刘强东",40,8000));
        list.add(new Employee(1010,"刘强东",41,8000));
        list.add(new Employee(1010,"刘强东",40,8000));
        list.add(new Employee(1010,"刘强东",40,8000));
        list.add(new Employee(1010,"刘强东",40,8000));

//        System.out.println(list);

        list.stream().distinct().forEach(employee -> System.out.println(employee.getName()+","+employee.getAge()));
    }


    @Test
    public void test6(){
        //map(Function f)——接收一个函数作为参数，将元素转换成其他形式或提取信息，该函数会被应用到每个元素上，并将其映射成一个新的元素。
        List<String> list = Arrays.asList("aa", "bb", "cc", "dd");
        list.stream().map(str->str.toUpperCase()).forEach(System.out::println);
        System.out.println("--------------------");
        //练习1：获取员工姓名长度大于3的员工的姓名。
        List<Employee> employees = EmployeeData.getEmployees();
        Stream<String> namesStream = employees.stream().map(Employee::getName);
        namesStream.filter(name -> name.length() > 4).forEach(System.out::println);
        System.out.println("--------------------");
        //2
        list.stream().map(StreamAPITest::fromStringToStream).forEach(s->{
            s.forEach(System.out::println);
        });

        System.out.println("--------------------");
        //        flatMap(Function f)——接收一个函数作为参数，将流中的每个值都换成另一个流，然后把所有流连接成一个流。
        list.stream().flatMap(StreamAPITest::fromStringToStream)
        .forEach(System.out::println);
        System.out.println("--------------------");

    }

    //将字符串中的多个字符构成的集合转换为对应的Stream的实例
    public static Stream<Character> fromStringToStream(String str){//aa
        ArrayList<Character> list = new ArrayList<>();
        for(Character c : str.toCharArray()){
            list.add(c);
        }
        return list.stream();

    }

    //1-匹配与查找
    @Test
    public void test7(){
        List<Employee> employees = EmployeeData.getEmployees();

//        allMatch(Predicate p)——检查是否匹配所有元素。
//          练习：是否所有的员工的年龄都大于18
        boolean allMatch = employees.stream().allMatch(e -> e.getAge() > 18);
        System.out.println(allMatch);

//        anyMatch(Predicate p)——检查是否至少匹配一个元素。
//         练习：是否存在员工的工资大于 10000
        boolean anyMatch = employees.stream().anyMatch(e -> e.getSalary() > 10000);
        System.out.println(anyMatch);

//        noneMatch(Predicate p)——检查是否没有匹配的元素。
//          练习：是否存在员工姓“雷”
        boolean noneMatch = employees.stream().noneMatch(e -> e.getName().startsWith("雷"));
        System.out.println(noneMatch);
//        findFirst——返回第一个元素
        Optional<Employee> employee = employees.stream().findFirst();
        System.out.println(employee.get().getName());
//        findAny——返回当前流中的任意元素
        Optional<Employee> employee1 = employees.parallelStream().findAny();
        System.out.println(employee1.get().getName());

    }

    @Test
    public void test8(){
        List<Employee> employees = EmployeeData.getEmployees();
        // count——返回流中元素的总个数
        long count = employees.stream().filter(e -> e.getSalary() > 5000).count();
        System.out.println(count);
//        max(Comparator c)——返回流中最大值
//        练习：返回最高的工资：
        Integer integer = employees.stream().map(e -> e.getSalary()).max(Integer::compare).get();
        System.out.println(integer);
//        min(Comparator c)——返回流中最小值
//        练习：返回最低工资的员工
        Optional<Employee> employee = employees.stream().min((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()));
        System.out.println(employee.get().getName());
        System.out.println();
//        forEach(Consumer c)——内部迭代
        employees.stream().forEach(System.out::println);

        //使用集合的遍历操作
        employees.forEach(System.out::println);
    }


    //2-归约
    @Test
    public void test9(){
//        reduce(T identity, BinaryOperator)——可以将流中元素反复结合起来，得到一个值。返回 T
//        练习1：计算1-10的自然数的和
        List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
        Integer sum = list.stream().reduce(0, Integer::sum); //第一个参数相当于初始值
        System.out.println(sum);


//        reduce(BinaryOperator) ——可以将流中元素反复结合起来，得到一个值。返回 Optional<T>
//        练习2：计算公司所有员工工资的总和
        List<Employee> employees = EmployeeData.getEmployees();
        Stream<Integer> salaryStream = employees.stream().map(Employee::getSalary);
//        Optional<Double> sumMoney = salaryStream.reduce(Double::sum);
        Optional<Integer> sumMoney = salaryStream.reduce((d1,d2) -> d1 + d2);
        System.out.println(sumMoney.get());

    }

    //收集

    /**
     * Collector 接口中方法的实现决定了如何对流执行收集的操作(如收集到 List、Set、Map)。
     *
     * 另外， Collectors 实用类提供了很多静态方法，可以方便地创建常见收集器实例，具体方法与实例如下表：
     */
    @Test
    public void test10(){
        List<Employee> list = EmployeeData.getEmployees();
        //toList
        List<Employee> collect = list.stream().collect(Collectors.toList());
        collect.forEach((e)->System.out.println(e.getName()));
        System.out.println("-------------------------");
        //toSet  略
        //把流中元素收集到创建的集合 toCollection
        ArrayList<Employee> collect1 = list.stream().collect(Collectors.toCollection(ArrayList::new));
        collect1.forEach((e)->System.out.println(e.getName()));
        System.out.println("-------------------------");
        //计算流中元素的个数 counting
        Long collect2 = list.stream().collect(Collectors.counting());
        System.out.println("元素的个数:"+collect2);
        System.out.println("-------------------------");
        //对流中元素的整数属性求和 summingInt
        Integer collect3 = list.stream().collect(Collectors.summingInt(Employee::getSalary));
        System.out.println("属性求和:"+collect3);
        System.out.println("-------------------------");
        //计算流中元素lnteger属性的平均值 averagingInt
        Double collect4 = list.stream().collect(Collectors.averagingInt(Employee::getSalary));
        System.out.println("属性的平均值:"+collect4);
        System.out.println("-------------------------");
        //收集流中lnteger属性的统计值。如：平均值
        IntSummaryStatistics collect5 = list.stream().collect(Collectors.summarizingInt(Employee::getSalary));
        System.out.println("属性的统计值。如：平均值:"+collect5.getAverage());
        System.out.println("-------------------------");
        //连接流中每个字符串
        String collect6 = list.stream().map(Employee::getName).collect(Collectors.joining());
        System.out.println("连接流中每个字符串:"+collect6);
        System.out.println("-------------------------");
        //根据比较器选择最大值
        Optional<Employee> collect7 = list.stream().collect(Collectors.maxBy(Comparator.comparingInt(Employee::getSalary)));
        System.out.println("根据比较器选择最大值:"+collect7.get().getName());
        System.out.println("-------------------------");
        //根据比较器选择最小值
        Optional<Employee> collect8 = list.stream().collect(Collectors.minBy(Comparator.comparingInt(Employee::getSalary)));
        System.out.println("根据比较器选择最小值:"+collect8.get().getName());
        System.out.println("-------------------------");
        //从一个作为累加器的初始值开始，利用BinaryOperator与流中元素逐个结合，从而归约成单个值
        Integer collect9 = list.stream().collect(Collectors.reducing(0, Employee::getSalary, Integer::sum));
        System.out.println("从而归约成单个值1:"+collect9);
        Integer reduce = list.stream().map(Employee::getSalary).reduce(0, Integer::sum);
        System.out.println("从而归约成单个值2:"+reduce);
        System.out.println("-------------------------");
        //包裹另一个收集器，对其结果转换函数
        Integer collect10 = list.stream().collect(Collectors.collectingAndThen(Collectors.toList(), List::size));
        System.out.println("对其结果转换函数:"+collect10);
        System.out.println("-------------------------");
        //根据某属性值对流分组，属性为K，结果为V
        Map<String, List<Employee>> collect11 = list.stream().collect(Collectors.groupingBy(Employee::getName));
        for (Map.Entry<String, List<Employee>> stringListEntry : collect11.entrySet()) {
            System.out.println("key:"+stringListEntry.getKey());
            System.out.println("value:"+stringListEntry.getValue().get(0).getName());
        }
        System.out.println("-------------------------");
        //根据true或false进行分区
        Map<Boolean, List<Employee>> collect12 = list.stream().collect(Collectors.partitioningBy(employee -> employee.getSalary() > 5000));
        for (Map.Entry<Boolean, List<Employee>> booleanListEntry : collect12.entrySet()) {
            System.out.println("key===="+booleanListEntry.getKey());
            for (Employee employee : booleanListEntry.getValue()) {
                System.out.println(employee.getSalary());
            }

        }
        System.out.println("-------------------------");
    }



}
